{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "41a004a3-d197-4a59-b63d-886faaf83ab8",
   "metadata": {},
   "source": [
    "# Jaguar Vector Store\n",
    "\n",
    "This document demonstrates llama_index working with Jaguar vector store.\n",
    "\n",
    "- It is a distributed vector database that can store large number of vectors.\n",
    "- The ZeroMove feature enables instant horizontal scaling.\n",
    "- It supports embeddings, text, images, videos, PDFs, audio, time series, and spatial data. \n",
    "- The all-master architecture allows both parallel reads and writes.\n",
    "- Its anomaly detection capabilities can distinguish outliers in the dataset.\n",
    "- The RAG support can combine LLMs and proprietary and real-time data.\n",
    "- Sharing of metadata across multiple vector indexes improves data consistency.\n",
    "- Distance metrics include Euclidean, Cosine, InnerProduct, Manhatten, Chebyshev, Hamming, Jeccard, and Minkowski.\n",
    "- Similarity search can be performed with time cutoff and time decay effects.\n",
    "\n",
    "## Prerequisites\n",
    "\n",
    "There are two requirements for running the examples in this file.\n",
    "\n",
    "You must install and set up the JaguarDB server and its HTTP gateway server. \n",
    "Please follow the instructions in [Jaguar Setup](http://www.jaguardb.com/docsetup.html) as a reference.\n",
    "\n",
    "You must install packages llama-index and jaguardb-http-client.\n",
    "\n",
    "    docker pull jaguardb/jaguardb_with_http\n",
    "    docker run -d -p 8888:8888 -p 8080:8080 --name jaguardb_with_http jaguardb/jaguardb_with_http\n",
    "    pip install -U llama-index\n",
    "    pip install -U jaguardb-http-client\n",
    "\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1c49fef-48b3-4319-8d3c-afdfeff1f832",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting jaguardb-http-client\n",
      "  Using cached jaguardb_http_client-3.4.1-py2.py3-none-any.whl (15 kB)\n",
      "Installing collected packages: jaguardb-http-client\n",
      "Successfully installed jaguardb-http-client-3.4.1\n"
     ]
    }
   ],
   "source": [
    "!pip install -U jaguardb-http-client"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e347858-487b-4776-ae5a-6ee52097a14a",
   "metadata": {},
   "source": [
    "## Imports\n",
    "The following packages should be imported. We use the OpenAIEmbedding as an example. You could choose other embedding models in your application."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "873e27a8-7807-4f21-b505-84aff5299262",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index import (\n",
    "    VectorStoreIndex,\n",
    "    ServiceContext,\n",
    "    SimpleDirectoryReader,\n",
    ")\n",
    "from llama_index.storage.storage_context import StorageContext\n",
    "from llama_index.embeddings import OpenAIEmbedding\n",
    "from llama_index.vector_stores.jaguar import JaguarVectorStore\n",
    "from jaguardb_http_client.JaguarHttpClient import JaguarHttpClient"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ae84951-c6cb-41c1-b02b-643128c639c8",
   "metadata": {},
   "source": [
    "## Client Object\n",
    "We now instantiate a jaguar vector store client object. The url is the http endpoint of the gateway server. The url should be replaced with your environment settings. The pod is the Pod (or database) name. The store is the name of the vector store. A pod may have multiple stores. The vector_index is the name of the vector index in the store. A store may have multiple vector indexes. The store client object is, however, bound to one vector index only. The vector_type specifies the attributes of the vector index. In the string \"cosine_fraction_short\", cosine means that the distance between two vectors is computed with the cosine distance. Fraction means the vector components are fractional numbers. Short means the storage format of the vector components is a short integer of signed 16-bits integers. Storage format could be float of 32-bit floating point numbers. It can also be a byte of 8-bit signed integers. The vector_dimension is the dimension of the vector generated by the provided embedding model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e2fc699-d38d-47df-9d73-0adb7b2331b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "url = \"http://127.0.0.1:8080/fwww/\"\n",
    "pod = \"vdb\"\n",
    "store = \"llamaindex_jaguar_store\"\n",
    "vector_index = \"v\"\n",
    "vector_type = \"cosine_fraction_float\"\n",
    "# vector_type = \"cosine_fraction_short\"  # half of memory usage compared to float\n",
    "# vector_type = \"cosine_fraction_byte\" # quarter of memory usage compared to float\n",
    "vector_dimension = 1536  # per OpenAIEmbedding model\n",
    "jaguarstore = JaguarVectorStore(\n",
    "    pod,\n",
    "    store,\n",
    "    vector_index,\n",
    "    vector_type,\n",
    "    vector_dimension,\n",
    "    url,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2c6991c-5223-459d-9247-32eb643a60cd",
   "metadata": {},
   "source": [
    "## Authentication\n",
    "The client must login or connect to back-end jaguar server for system security and user authentication. Environment variable JAGUAR_API_KEY or file $HOME/.jagrc file must  contain the jaguar api ke issued by your system administrator. The login() method returns True or False. If it returns False, then it may mean that your jaguar api key is invalid, or the http gateway server is not running, or the jaguar server is not running properly.\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54e667f0-934b-4d9a-9056-5c7a0cc4f9e6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "login result is True\n"
     ]
    }
   ],
   "source": [
    "true_or_false = jaguarstore.login()\n",
    "print(f\"login result is {true_or_false}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d305ef91-2de1-4754-af22-dca1e96ec4c2",
   "metadata": {},
   "source": [
    "## Create Vector Store\n",
    "We now create a vector store with a field 'v:text' of size 1024 bytes\n",
    "to hold text, and two additional metadata fields 'author' and 'category'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "179a90fc-47a0-45c7-b116-3a5fe3b14adc",
   "metadata": {},
   "outputs": [],
   "source": [
    "metadata_str = \"author char(32), category char(16)\"\n",
    "text_size = 1024\n",
    "jaguarstore.create(metadata_str, text_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "558a5fbb-5cee-4782-8934-cefb0af22822",
   "metadata": {},
   "source": [
    "## Load Documents\n",
    "The following code opens the example Paul Gram documents and read them into memory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "851a2625-0e5f-4b9d-aa09-77eed9f46efc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loading 1 doument(s)\n"
     ]
    }
   ],
   "source": [
    "documents = SimpleDirectoryReader(\"../data/paul_graham/\").load_data()\n",
    "print(f\"loading {len(documents)} doument(s)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d072cc6-6e93-42aa-9b73-be3025a8190a",
   "metadata": {},
   "source": [
    "## Make Index\n",
    "Prepare storage context, service context, and make an index object. After the call of from_documents(), there will be 22 vectors saved in the vector store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "542cd51b-2aa1-4ff6-b4d5-35b2ac67de61",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "There are 22 vectors in jaguar vector store\n"
     ]
    }
   ],
   "source": [
    "### make a storage context using our vector store\n",
    "storage_context = StorageContext.from_defaults(vector_store=jaguarstore)\n",
    "\n",
    "### have a service context using the openai embedding model\n",
    "embed_model = OpenAIEmbedding()\n",
    "service_context = ServiceContext.from_defaults(embed_model=embed_model)\n",
    "\n",
    "### clear all vectors in the vector store\n",
    "jaguarstore.clear()\n",
    "\n",
    "### make an index with the documents,storage context, and service context\n",
    "index = VectorStoreIndex.from_documents(\n",
    "    documents, storage_context=storage_context, service_context=service_context\n",
    ")\n",
    "\n",
    "### You could add more documents to the vector store:\n",
    "# jaguarstore.add_documents(some_docs)\n",
    "# jaguarstore.add_documents(more_docs, text_tag=\"tag to these documents\")\n",
    "\n",
    "### print number of documents in jaguar vector store\n",
    "num = jaguarstore.count()\n",
    "print(f\"There are {num} vectors in jaguar vector store\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afebb7c0-47f5-4df7-a571-b23628176fb7",
   "metadata": {},
   "source": [
    "## Ask Questions\n",
    "We get a query engine and ask some questions to the engine."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a37cf316-01c0-4ade-8fbc-e8e5f7ab76c1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: What did the author do growing up?\n",
      "Answer: The author mentioned that growing up, they worked on two main things outside of school: writing and programming. They wrote short stories and tried writing programs on an IBM 1401 computer.\n",
      "\n",
      "Question: What did the author do after his time at Viaweb?\n",
      "Answer: After his time at Viaweb, the author started a company to put art galleries online. However, this idea did not turn out to be successful as art galleries did not want to be online.\n"
     ]
    }
   ],
   "source": [
    "query_engine = index.as_query_engine()\n",
    "q = \"What did the author do growing up?\"\n",
    "print(f\"Question: {q}\")\n",
    "response = query_engine.query(q)\n",
    "print(f\"Answer: {str(response)}\\n\")\n",
    "\n",
    "q = \"What did the author do after his time at Viaweb?\"\n",
    "print(f\"Question: {q}\")\n",
    "response = query_engine.query(q)\n",
    "print(f\"Answer: {str(response)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32d0fb08-1860-494a-bac8-f3ead4d0af3a",
   "metadata": {},
   "source": [
    "## Pass Query Options\n",
    "We can pass extra arguments to the query engine to select only a subset of data from the jaguar vector store. This can be achieved by using the `vector_store_kwargs` argument. Parameter day_cutoff is number of days beyond which text will be ignored. day_decay_rate is rate of daily decay for similarity scores.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5003795-40cf-44e6-a366-f45d98acee22",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: What was the author's life style?\n",
      "Answer: The author's lifestyle involved attending the Accademia as a student and painting still lives in their bedroom at night. They also wrote essays and had a messy life, which they thought would be interesting and encouraging to others.\n"
     ]
    }
   ],
   "source": [
    "qkwargs = {\n",
    "    \"args\": \"day_cutoff=365,day_decay_rate=0.01\",\n",
    "    \"where\": \"category='startup' or category=''\",\n",
    "}\n",
    "query_engine_filter = index.as_query_engine(vector_store_kwargs=qkwargs)\n",
    "q = \"What was the author's life style?\"\n",
    "print(f\"Question: {q}\")\n",
    "response = query_engine_filter.query(q)\n",
    "print(f\"Answer: {str(response)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13233170-ad42-48e7-9de6-9ba52a6070f1",
   "metadata": {},
   "source": [
    "## Cleanup and Logout\n",
    "All vectors and related data in the vector store can be deleted and the vector store can be removed completely to finish the test. Logout call makes sure resources used by the client are released."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d162991-b128-4451-94a9-8ca363cbf4c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "### remove all the data in the vector store if you want\n",
    "jaguarstore.clear()\n",
    "\n",
    "### delete the whole vector in the database if you want\n",
    "jaguarstore.drop()\n",
    "\n",
    "### disconnect from jaguar server and cleanup resources\n",
    "jaguarstore.logout()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
